#pragma once

#include "Containers/Vector.h"
#include "ByteCodeFile.h"

class CBytecodeWriter
{
public:    
    CBytecodeWriter(std::shared_ptr<CByteCodeFile> InFile)
        :ByteCode(InFile)
    {

    }

protected:
    friend class CByteCodeReader;

    //string table , use a uint32_t to index the string table
    TVector<CString> StringTable; 

    friend class CByteCodeGenerator;

    TVector<CByteCodeFile::SSourceCodeFile> SourceCodeFileTable;

    std::shared_ptr<CByteCodeFile> ByteCode;

    CBytecodeWriter& operator <<(EByteCodeInstructions Instruction);

    CBytecodeWriter& operator <<(EByteCodeSectionType SectionType);

    CBytecodeWriter& operator <<(STypeID Value);

    CBytecodeWriter& operator <<(SFunctionID Value);

    CBytecodeWriter& operator <<(const TVector<uint32_t>& Value);

    CBytecodeWriter& operator <<(bool Value);

    CBytecodeWriter& operator <<(char32_t Value);

    CBytecodeWriter& operator <<(int8_t Value);

    CBytecodeWriter& operator <<(int16_t Value);

    CBytecodeWriter& operator <<(int32_t Value);

    CBytecodeWriter& operator <<(int64_t Value);

    CBytecodeWriter& operator <<(uint8_t Value);

    CBytecodeWriter& operator <<(uint16_t Value);

    CBytecodeWriter& operator <<(uint32_t Value);
    
    CBytecodeWriter& operator <<(uint64_t Value);    

    CBytecodeWriter& operator <<(float Value);

    CBytecodeWriter& operator <<(double Value);

    CBytecodeWriter& operator <<(const CString& Value);

    CBytecodeWriter& operator <<(const CConstantValue& Value);

    CBytecodeWriter& operator <<(const CByteCodeFile::SBreakpointInfo & BreakpointInfo);

    //CBytecodeWriter& operator <<(const CString& Value);

    CBytecodeWriter& operator <<(const TVector<int64_t>& Value);

    CBytecodeWriter& operator <<(const TVector<CString>& Value);

    CBytecodeWriter& operator <<(const CByteCodeFile::STypeInfo & TypeInfo);

    CBytecodeWriter& operator <<(const CByteCodeFile::SVariableInfo& VariableInfo);

    CBytecodeWriter& operator <<(const TVector<CByteCodeFile::STypeInfo>& TypeInfos);

    CBytecodeWriter& operator <<(EScriptAccessLevel AccessLevel);

    CBytecodeWriter& operator <<(const CByteCodeFile::SMemberDecorator& Decorator);

    CBytecodeWriter& operator <<(const CByteCodeFile::SFunctionCallInfo& FunctionCallInfo);

public:
    uint32_t GetByteCodeSize() const;

    void Finish();

    //return the index of the namespace instruction
    uint32_t BeginNamespace(const CString& NamespaceName, const TVector<CString> &  InParentNamespaceID );

    //end the namespace
    void EndNamespace(uint32_t NamespaceIndex);

    //make inner type searchable
    //void AddInnerType(const CString& TypeName);

    //return the index of the class instruction
    uint32_t BeginClass(const CByteCodeFile::SClassInfo& InClassInfo);

    //end the class
    void EndClass(uint32_t ClassIndex);

    void AddGeneralizedClass(const CByteCodeFile::SGeneralizedClasseInfo& GeneralizedClasses);

    uint32_t BeginClassContent(STypeID ClassID);

    void EndClassContent(uint32_t ClassContentIndex);

    void AddFunctionType(const CByteCodeFile::SFunctionType& InFunctionType);

    //add field to the class
    void AddField(const CByteCodeFile::SFieldInfo& FieldInfo);

    //return the index of the function instruction
    uint32_t BeginFunction();

    //end the function
    void EndFunction(uint32_t FunctionIndex,const CByteCodeFile::SFunctionInfo& FunctionInfo );

    //void SetLocalVariables(const TVector<STypeInfo>& TypeInfo);


    void AddVirtualFunctionTable(const TVector<SFunctionID>& VirtualFunctionTable);

    void AddBinocularOperation(EByteCodeInstructions Operator);

    void AddLoadLiteralOperation(uint8_t InValue);
    void AddLoadLiteralOperation(uint16_t InValue);
    void AddLoadLiteralOperation(uint32_t InValue);
    void AddLoadLiteralOperation(uint64_t InValue);
    void AddLoadLiteralOperation(int8_t InValue);
    void AddLoadLiteralOperation(int16_t InValue);
    void AddLoadLiteralOperation(int32_t InValue);
    void AddLoadLiteralOperation(int64_t InValue);
    void AddLoadLiteralOperation(bool InValue);
    void AddLoadLiteralOperation(float InValue);
    void AddLoadLiteralOperation(double InValue);
     void AddLoadLiteralOperation(const char32_t& InValue);
    void AddLoadLiteralOperation(const CString& InValue);

    void AddStoreVariableOperation(uint32_t VariableIndex);

    void AddLoadVariableOperation(uint32_t VariableIndex);

    void AddLoadVariableAddressOperation(uint32_t VariableIndex);

    void AddFunctionCallOperation(const CByteCodeFile::SFunctionCallInfo& FunctionCallInfo);
    
    void AddVirtualFunctionCallOperation(const CByteCodeFile::SFunctionCallInfo& FunctionCallInfo);

    void AddLoadArgumentOperation(uint32_t ParameterIndex);

    void AddLoadArgumentAddressOperation(uint32_t ParameterIndex);

    void AddStoreArgumentOperation(uint32_t ParameterIndex);

    void AddBoxOperation(const CByteCodeFile::STypeInfo& TypeInfo);

    void AddStoreFieldOperation(uint32_t FieldIndex);

    void AddStoreStaticFieldOperation(const STypeID& Class , uint32_t FieldIndex);

    void AddLoadFieldOperation(uint32_t FieldIndex);

    void AddLoadFieldAddressOperation(uint32_t FieldIndex);

    void AddLoadStaticFieldOperation(const STypeID& Class ,  uint32_t FieldIndex);

    void AddLoadStaticFieldAddressOperation(const STypeID& Class ,  uint32_t FieldIndex);

    void AddNewObjectOperation(const CByteCodeFile::SFunctionCallInfo& TypeInfo);

    void AddInitValueTypeOperation(const STypeID& InType);

    void AddReturnOperation();

    void AddDoNothingOperation();

    void AddJumpOperation(uint32_t NewLineIndex,EByteCodeInstructions InInstruction);

    void AddInstruction(EByteCodeInstructions Instruction);

    void AddInnerTypeConvertion(EScriptInnerType FromType, EScriptInnerType ToType);

    void AddLoadFunctionOperation(const CByteCodeFile::SFunctionLoadInfo & FunctionLoadInfo); 

    void AddCastClassOperation(const STypeID& Class);
};